#include "M7engine.h"

namespace M7engine
{
Engine::Engine()
{
}

Engine::~Engine()
{
	delete inputManager;
	delete soundManager;
	delete fontManager;
	delete primitives;
	delete collisionManager;
	delete cameraManager;

	al_destroy_bitmap(this->icon);
    al_destroy_timer(this->timer);
    al_destroy_display(this->display);
    al_destroy_event_queue(this->eventQueue);
}

bool Engine::init(int width, int height, bool fullscreen)
{
    this->setScreenWidth(width);
    this->setScreenHeight(height);

    frameRate = 30;
    frameCount = 0;

    redraw = true;

    if (!al_init())
    {
        fprintf(stderr,"al_init failed\n");
        return false;
    }

    inputManager = new InputManager;
    inputManager->init();

    soundManager = new SoundManager;
    soundManager->init();

    fontManager = new FontManager;
    fontManager->init();

    primitives = new Primitives;
    primitives->init();

    collisionManager = new CollisionManager;

    cameraManager = new CameraManager;
    cameraManager->init();

    al_init_image_addon();

    timer = al_create_timer(1.0/this->getFrameRate());
    if (!timer)
    {
        fprintf(stderr, "failed to create game loop timer\n");
        return false;
    }

    if (fullscreen)
    {
        al_set_new_display_flags(ALLEGRO_FULLSCREEN);
    }

    display = al_create_display(width, height);
    if (!display)
    {
        fprintf(stderr,"failed to create main display\n");
        al_destroy_timer(timer);
        return false;
    }

    eventQueue = al_create_event_queue();
    if (!eventQueue)
    {
        fprintf(stderr,"failed to create main event queue\n");
        al_destroy_display(display);
        al_destroy_timer(timer);
        return false;
    }

    al_register_event_source(eventQueue, al_get_display_event_source(display));
    al_register_event_source(eventQueue, al_get_timer_event_source(timer));
    al_register_event_source(eventQueue, al_get_mouse_event_source());

    al_clear_to_color(al_map_rgb(0,0,0));
    al_flip_display();

    al_start_timer(timer);
    return true;
}

bool Engine::update()
{
    inputManager->update();

    frameCount++;

    ALLEGRO_EVENT event;
    al_wait_for_event(eventQueue, &event);

    switch (event.type)
    {
    case ALLEGRO_EVENT_TIMER:
        redraw = true;
        break;
    case ALLEGRO_EVENT_DISPLAY_CLOSE:
    	return false;
    	break;
    }

    if (redraw && al_is_event_queue_empty(eventQueue))
    {
        redraw = false;

        double time = al_get_time();

        updateCollisions();
        updateEntities();
        drawEntities();

        cameraManager->drawCameras(display);

        al_flip_display();
        al_clear_to_color(al_map_rgb(0,255,255));

        fpsAccum++;
        if (time - fpsTime >= 1)
        {
        	fps = fpsAccum;
        	fpsAccum = 0;
        	fpsTime = time;
        }
    }

    this->cleanEntities();

    return true;
}

void Engine::updateEntities()
{
    std::list<Entity*>::iterator iter;
    Entity *entity;
    iter = entities.begin();

    while (iter != entities.end())
    {
        entity = *iter;
        if (entity->getActive())
        {
            entity->update();
            entity->animate(fpsTime);
        }
        iter++;
    }
}

void Engine::drawEntities()
{
    std::list<Entity*>::iterator iter;
    Entity *entity;
    iter = entities.begin();

    while (iter != entities.end())
    {
        entity = *iter;
        if (entity->getVisible())
        {
            entity->draw();
        }
        iter++;
    }
}

void Engine::cleanEntities()
{
    std::list<Entity*>::iterator iter;
    Entity *entity;
    iter = entities.begin();

    while (iter != entities.end())
    {
        entity = *iter;
        if (!entity->getActive())
        {
            entities.erase(iter);
        }
        else
        {
            iter++;
        }
    }
}

void Engine::addEntity(Entity *entity)
{
    static int id = 0;
    entity->setID(id);
    entities.push_back(entity);
    id++;
}

Entity* Engine::findEntity(int id)
{
    std::list<Entity*>::iterator iter;
    Entity *entity;
    while (iter != entities.end())
    {
        entity = *iter;
        if (entity->getID() == id)
        {
            return entity;
        }
        else
        {
            iter++;
        }
    }

    return NULL;
}

void Engine::updateCollisions()
{
    std::list<Entity*>::iterator iterA, iterB;
    Entity *entityA;
    Entity *entityB;
    iterA = entities.begin();
    iterB = entities.begin();

    while (iterA != entities.end())
    {
        entityA = *iterA;

        while (iterB != entities.end())
        {
            entityB = *iterB;
            if (collisionManager->getCollisionBBox(entityA, entityB))
            {
                entityA->collision(entityB);
            }
            iterB++;
        }
        iterB = entities.begin();
        iterA++;
    }
}

};
